Due Date Notifier – an Asynchronous Task Example


I always wanted to look into asynchronous tasks and also in creating events and e-mails on the server. Especially mail notification is a popular request on the forum. So here a working example.

Origin – Other Posts Around This Topic

This is not all my work, it is based on the QueryDev Guide in the the Jazz Developer Wiki and on Jorge Diaz’ blog post “RTC Custom Scheduled Async Task example explained”. I was looking into these sources, but I found the QueryDev Guide did not ship a complete example – it was the basic classes. Jorge ships the extension, but both examples don’t compile with the RTC 6.x versions of RTC and potentially earlier versions. As mentioned in the QueryDev Guide the feed category used there is no longer available and in both posts one of the API methods used is missing a parameter. This one of the few examples where I have actually seen minor changes in the API so far.

This gave me enough reasons to look into creating a working example again, to refine it and to try to better understand what the extension does and how.

Jorge has an additional post around asynchronous tasks. See Custom Services Configuration Properties – naming collision issues for additional hints.

License and Download

The post contains published code, so our lawyers reminded me to state that the code in this post is derived from examples from Jazz.net as well as the RTC SDK. The usage of code from that example source code is governed by this license. Therefore this code is governed by this license. I found a section relevant to source code at the and of the license. Please also remember, as stated in the disclaimer, that this code comes with the usual lack of promise or guarantee. Enjoy!

You can download the project source code from here.

Just Starting With Extending RTC?

If you just get started with extending Rational Team Concert, or create API based automation, start with the post Learning To Fly: Getting Started with the RTC Java API’s and follow the linked resources.

You should be able to use the following code in this environment and get your own automation or extension working.

The example in this blog post shows RTC Server and Common API.

Solution Outline

The code implements an asynchronous task that is deployed on the server and uses a query to find work items that are overdue and not closed, in any project area. It creates a notification for these work items. The solution runs in two configurable modes.

  1. Mode “event” is the default. In this mode the asynchronous task creates a Message At Me event in the server feed using the IWorkItemFeedConstants.MESSAGES_AT_ME_FEED_CATEGORY. This event also triggers E-Mail notification for the owner. The Event is only created if there is no related MESSAGES_AT_ME_FEED_CATEGORY event that has been created in a configurable time span. This prevents from flooding the events and the user e-mail from duplicates.
  2. Mode “mail” can be configured. This mode sends a dedicated mail to the owner of the work item. This mode can not detect if it has recently sent a mail to the user. This means the asynchronous task should run with a delay that prevents it from running multiple times a day to avoid spamming the mail folders. It would also be worthwhile to look into a way to schedule the task only at a certain time of the day.

The reason why the second mode was added is two fold.

  1. I wanted to have an example how to send an e-mail with the server API.
  2. The MESSAGES_AT_ME_FEED_CATEGORY creates a mail that has a subject similar to

    “You were mentioned in work item ’52: This is a test work item’ [s]”.

    You can see the example message below. Althought he message body mentions its cause, this message is not really specific in its topic and I rather wanted something more specific.

Message Generated By Event

I tested several different event categories, including a custom one, but the results where even worse. For example, the message body did not even contain the event description “The work item was due 6 weeks 3 days 4 hours ago”.

The custom message sent in the second mode looks like this.

Custom Message

Since this is only an example, you might want to think about how to avoid sending multiple e-mails e.g. somehow storing information when a message was sent to whom and for which work item somewhere.

I tried if I could create an event, that would not trigger an e-mail to store the information, but that did not work for me.

Set-up and Use a Test Mail Server and Client

You probably don’t want to run your tests against the company mail server. To be able to test and debug the extension, it is better to install a test server. The same probably applies to the business mail client, so consider to install a test mail client.

There are various free mail servers and mail clients available out there. I used hMailServer as mail server on Windows for my testing. I used Mozilla Thunderbird as mail client for testing.

Set up the mail server and create user mail accounts to be used for testing. Then make sure to be able to to connect the mail client to the mail server and send mails back and forth. This provides all the information needed to set up RTC for mail notification.

To verify the required settings needed for the RTC/JTS server, enable e-mail notification on the development server (the one that is installed in the folder C:\RTCxxDev\installs\JazzTeamServer). Start the development server, if it is not already running.  Open the JTS setup page https://localhost:9443/jts/setup.  Proceed to the step Configure E-mail Notification. Enter the values needed to configure the development test server to enable mail notification. Test the connection. The page should look similar to the one below with different values. Note that you can use domain names or configure IP aliases in your hosts file to avoid using localhost as the server address.

Mail Notification SetupCreate test users with e-mail accounts matching your test accounts on the test mail server. Send test e-mails e.g. invitations for a user to make sure everything works. Note that there is a delay in the server, you would have to wait a while to receive the e-mail. Check the JTS server log for any errors with respect to mail sending if needed.

If this is working, open the JTS teamserver.properties file in C:\RTCxxDev\installs\JazzTeamServer\server\conf\jts to locate the values that were added. The values corresponding to the setup above are shown below.

E-mail settings in teamserver.properties FileThe entries should all start with the prefix com.ibm.team.repository.notification. The best option to locate the values is to search for that prefix, as the properties are not saved in an alphabetic order and will be spread across the file.

Copy the entries into some text editor, as the are needed to set up and enable e-mail notification in the Jetty server debug launch.

How to Test and Debug an Asynchronous Task

For debugging it is important to enable e-mail notification in the Jetty debug server. So it is necessary to provide the inforation above to enable the notification.

The Jetty launch files in the Rational Team Concert Extensions Workshop have a teamserver.property file located in the project RTC Extension Workshop Configuration in the subfolder jazz.

Jetty Configuration teamserver.properties file It would be possible to use this file to pass the special configuration parameters. But the file would be shared between all the launches that are configured this way.

As an alternative, it is possible to specify properties in the launch itself. This makes it possible to have various different launches that manage the different setups. Since a launch file can be stored in a project related to the extension itself, this is a way better option and also allows to share the launch in SCM and keep the RTC Extension Workshop Configuration on default values. This is the approach used here.

To configure e-mail notification and other properties, create your own Jetty launch, by copying and giving it a reasonable name. Then add the properties needed to the System Properties of the Jetty Launch.  The next image shows such settings. Use the common tab of the launch to change the save location and save it in one of your extension projects.

Mail Notification Setup in Jetty LaunchTo be able to test and debug an asynchronous task, you want to be able to run the task more frequently as in a typical deployment. To avoid having an unrealistic default setting for the frequency of the task in the production environment, it must be avoided to set the default to a high frequency. This can be solved by configuring the task frequency by adding a configuration property in the same place that was used above.

The special property to change the delay between running the asynchronous task is basically the property that the server would save in the teamserver.properties if the task default would be overwritten. The ID of the property depends on the ID of the Jazz component defined in the task and on the name of the property.

Asynchronous Task Configuration Override PropertyIn the case of this example the component ID is com.ibm.js.team.workitem.extension.duedatenotifier.component

The name of the property is task.remind.fixedDelay.

So the ID of the property is com.ibm.js.team.workitem.extension.duedatenotifier.component.task.remind.fixedDelay

In the image below, the value is set to 10 so that the tasks is run every 10 seconds.

Configuration Parameters in the Server Launch

As shown in the image above there are other parameters for the asynchronous task that can be overwritten for testing in a similar way.

The mail task also does not run very often and for debugging it would be convenient if it runs more frequently. This can be fixed similar to above. The parameter com.ibm.team.workitem.notification.mail.fixedDelay=15 at the end of the  list in the above image is used to set the mailer mail sending frequency to 15 seconds, to avoid to have to wait 10 minutes or whatever the default is until the messages are actually sent.

A launch with these settings is shipped as part of the code. You have to change the e-mail settings for your setup to make it work though.

Launch Included in ProjectChanges in the Code

The documentation in the QueryDev Guide in the the Jazz Developer Wiki is still valid for the code that can be downloaded from this post. There are some few changes that were done.

A new configuration property with name task.remind.notification.type was introduced as shown below.

Additional Configuration ParameterIt can have the following values

  • event (default) – Create notification events, user will get e-mail if configured
  • mail – Create a special e-mail and always send it to the user; run the task only once  day in this mode

The new capability to send a special e-mail was introduced based on Jorge Diaz’ blog post “RTC Custom Scheduled Async Task example explained”. To support sending e-mails additional prerequisites where added to the Extension as well.

There is some more documentation added in the code.

In the method runTask() a decision is taken to create events or to send a special e-mail, based on the configuration.

/*
 * (non-Javadoc)
 * 
 * @see
 * com.ibm.team.repository.service.async.AbstractAutoScheduledTask#runTask()
 */
@Override
protected void runTask() throws TeamRepositoryException {
	IProgressMonitor monitor = new NullProgressMonitor();

	// get the configuration for the notification
	getEventConfiguration();
	// Get the attributes and durations for the reminders
	List reminders = getReminders();
	// Get the project areas to be able to query the work items
	List<IProjectAreaHandle> projectAreas = findProjectAreas(monitor);
	// Get the recent notifications to be able to suppress additional
	// reminders
	Set<UUID> recentNotifications = findRecentNotifications(monitor);

	// iterate all the project areas
	for (IProjectAreaHandle projectArea : projectAreas) {
		// For each configured reminder
		for (Reminder reminder : reminders) {
			// get the work items that qualify
			List<IWorkItem> workItems = findWorkItems(projectArea,
					reminder, monitor);
			if (!workItems.isEmpty()) {
				// If there are work items, get the attribute for the
				// reminder
				IAttribute attribute = getWorkItemCommon().findAttribute(
						projectArea, reminder.attributeId, monitor);
				// For each work item found
				for (IWorkItem workItem : workItems) {
					if (enableMail) {
						createNotificationMail(workItem, attribute);
					} else {
						// If there is no recent notification
						if (!recentNotifications.contains(workItem
								.getItemId()))
							// Create one as a feed
							createNotificationEvent(workItem, attribute);
					}
				}
			}
		}
	}
}

The code that implements sending the e-mail in Jorge Diaz’ blog post “RTC Custom Scheduled Async Task example explained” is shown below. Jorge’s code was changed to be able to show the URL of the work item in the e-mail. It also does not send mail to the owner if there is no owner (the owner has the ID unassigned).

This might be language dependent. I did not find a great way to get the unassigned user. Please leave better ways to do this in a comment, if you come across one.

It would be possible to send the e-mail to some other account in case it has no owner. It would be possible to search for a user with a certain role such as ‘Project Manager’ and make sure that the task is not forgotten.

/**
 * Creates a custom notification mail to the owner.
 * 
 * @param workItem
 * @param attribute
 * @throws TeamRepositoryException
 */
private void createNotificationMail(IWorkItem workItem, IAttribute attribute)
		throws TeamRepositoryException {
	IContributorHandle ownerHandle = workItem.getOwner();
	if (ownerHandle != null) { // We need to have an owner
		IContributor owner = (IContributor) getRepositoryService().fetchItem(
				ownerHandle, IRepositoryItemService.COMPLETE);
		if(owner.getUserId().equalsIgnoreCase(UNASSIGNED_USER_ID)){
			// You might want to do something, e.g. annoy someone else.
			return;
		}
		String eMail = owner.getEmailAddress();
		sendRememberingMail(workItem, attribute, eMail);
	}
}

The below code creates the message.

/**
 * Create an e-mail and send it around.
 * 
 * @param workItem
 * @param attribute
 * @param mail
 * @throws TeamRepositoryException
 */
private void sendRememberingMail(IWorkItem workItem, IAttribute attribute,
		String mail) throws TeamRepositoryException {
	Location location = Location.namedLocation(workItem,
			this.getRequestRepositoryURL());
	String itemLocation = location.toString();
	long diff = getDueDifference(workItem, attribute);
	Duration duration = new Duration(Math.abs(diff) / HOUR * HOUR);

	String attributeSuffix = "";
	if (!attribute.getIdentifier().equals(IWorkItem.DUE_DATE_PROPERTY)) {
		attributeSuffix = " for work item attribute: "
				+ attribute.getDisplayName();
	}
	String mailSummary = "'" + workItem.getId() + ": "
			+ workItem.getHTMLSummary().getPlainText() + "'"
			+ attributeSuffix;

	String subject = NLS.bind("Due Date Reminder Notification - {0}",
			mailSummary);
	String message = NLS.bind("Work item {0}  was due {1} ago",
			itemLocation, fDurationFormat.format(duration));
	MailSender sender = getMailService().getDefaultSender();

	try {
		getMailService().sendMail(sender, mail, subject, message, null);
	} catch (MessagingException e) {
		String warningMessage = NLS
				.bind("Failed to send DueDate Notification message of work item {0} to {1}",
						workItem.getHTMLSummary().toString(), mail);
		getLog().warn(warningMessage, e);
	}

}

The interesting part is to create the URL for the work item. It uses the class Location to get the URI (URL) created. It is important to note that the example uses a named location. The related method namedLocation() is used to create the Location. There are different types of locations and the URIs that are created are slightly different dependent of the location type. In some contexts it is important to use the correct location. One example is in creating links between artifacts of different applications such as requirements and test artifacts. Using the wrong location type will cause problems. In this case converting the location to a string creates the link to open the work item we want to be available in the mail.

Location location = Location.namedLocation(workItem,this.getRequestRepositoryURL());
String itemLocation = location.toString();

Packaging

The structure of the projects that are used to ship is similar to all the recent examples posted on this blog.

  • com.ibm.js.team.workitem.extension.duedatenotifier.common: contains the extension for the component
  • com.ibm.js.team.workitem.extension.duedatenotifier.service: contains the extension that implements the service and refers back to the component
  • com.ibm.js.team.workitem.extension.duedatenotifier.server.feature: defines the feature, combining the component and the service
  • com.ibm.js.team.workitem.extension.duedatenotifier.server.feature.updateSite: is the update site project to build the extension
  • com.ibm.js.team.workitem.extension.duedatenotifier.server.feature.deploy: is the deploy project that defines the additional files and folders for deployment such as the provision profile and the site folder

Deploying the Extension

To build and deploy the task, open the site.xml in tha update site project and run Build All. Then copy the site.xml file and the folders features and plugins into the folder sites/js_wi_duedate_notifier_task in the deploy project.

Once this is done, it is possible to copy and paste the whole content of the deploy project into the configuration folder server/conf/ccm and accept any overwrites.

The image below shows the first part.

Packaging of ProjectsSee the post Is The Extension Deployed? How Can I Redeploy? to understand how to reset the server to enforce deploying the new extension.

Note, all projects are set up to ignore the files that don’t need to be under version control in RTC.

Configure the Due Date Notifier in Production

The Due Date Notifier Asynchronous Task can be configured in the advanced properties of the CCM server it is deployed.

Configure Advanced Properties for Notifier

Summary

The code is experimental. I have tested it in a Jetty based test server using the Scrum template. It is by no means production ready and can be enhanced for various scenarios. However, as always, I hope the code is an inspiration and helps someone out there to save some time. If you are just starting to explore extending RTC, please have a look at the hints in the other posts in this blog on how to get started.

17 thoughts on “Due Date Notifier – an Asynchronous Task Example

    • Steve, I have not tested which user and permission this is running in. I haven’t tested if you can e.g. change work items there. Would be interesting to know what context that is going to be.

      • I am not sure what you are asking for. I have learned over the years that it will never be possible to have everything users want in the products. It is always a tradeoff.
        The UI shows work items as overdue and you can run queries too and have them on your dashboard. This is how RTC is supposed to be used, really. Flooding the users inbox with mails is not necessarily what you want, although it seems to be very popular.

        You can however, always ask for an enhancement.

      • By the way, I chose this as an example, because the code was already there and I wanted to have a working example that users could download. I also wanted to show the code for sending e-mail on this blog.

        Asynchronous tasks could be useful for many things that should run in the background and therefore I thought a working example would be useful as well.

  1. Is due date notifier participant or advisor?I have deployed plugin on server but unable to see the same in advance properties of RTC.Do i need to set some additional properties?

    • You seem not to have followed the post at all and used Jetty first, You seem to lack basic knowledge about log files. You don’t provide any information at all. So my suggestion, you start at the beginning and setup a development environment and follow the link for beginners.

  2. After trying the steps suggested by you,and following the beginners link.
    I am getting the strange error in ccm log file.

    Content of log file:

    Unable to discover the GC service, which might indicate that Global Configuration Management is not set up in JTS. Please have your admin verify that your GC server is running, and that it is registered with JTS.
    2018-08-23 19:38:31,980 [ccm: AsynchronousTaskRunner-2 @@ 19:38] WARN ervice.internal.GlobalConfigurationCacheUpdateTask – CRJGC1601W Duplicate log entries (count=60) for operationId=550 were omitted to avoid flooding the log, but the condition still exists.
    2018-08-23 19:38:31,980 [ccm: AsynchronousTaskRunner-2 @@ 19:38] ERROR ervice.internal.GlobalConfigurationCacheUpdateTask – [operationId=611] com.ibm.team.jfs.app.http.HttpServiceUnavailableException: Unable to discover the GC service, which might indicate that Global Configuration Management is not set up in JTS. Please have your admin verify that your GC server is running, and that it is registered with JTS.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.